package com.zmn.plat.business.impl.pic;

import static java.lang.System.out;

import java.awt.image.BufferedImage;
import java.io.*;

import javax.imageio.ImageIO;

import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.constant.StatusConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dto.AliyunOssStsDTO;
import com.zmn.common.dto.ResultDTO;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.plat.business.interfaces.pic.PicUploadBService;

import net.coobird.thumbnailator.Thumbnails;

/**
 * https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.711.47a02d36ve1TRb
 *
 * @author duanzuocai
 * @version v1.0
 * @类说明：1、环境目录，2、业务目录，3、文件名
 * @since 2018/11/20 10:58
 */
@Service
public class PicUploadBServiceImpl implements PicUploadBService {
    private Logger logger = LoggerFactory.getLogger(PicUploadBServiceImpl.class);

    private final String URL_ERROR_SPLIT = "\\";

    private final String GIF_PICTURE = ".gif";

    @Value("${ali.yun.oss.endpoint.sts}")
    String stsEndpoint;
    @Value("${ali.yun.oss.endpoint}")
    String endpoint;
    @Value("${ali.yun.oss.accessKeyId}")
    String accessKeyId;
    @Value("${ali.yun.oss.accessKeySecret}")
    String accessKeySecret;
    @Value("${ali.yun.oss.bucketName}")
    String bucketName;
    @Value("${ali.yun.oss.profile}")
    String profile;
    @Value("${ali.yun.oss.roleArn}")
    String roleArn;
    @Value("${ali.yun.oss.pic.width}")
    Integer width;
    @Value("${ali.yun.oss.pic.height}")
    Integer height;

    /**
     * 阿里云oss文件域名
     */
    private static String ALI_YUN_OSS_FILE_BASE_URL = "https://file1.xiujiadian.com";

    private final String product = "Sts";

    @Override
    public ResultDTO uploadPic(String directory, String fileName, File file) {
        return uploadPic(directory, fileName, file, null, null);
    }

    @Override
    public ResultDTO uploadPic(String directory, String fileName, File file, Integer maxWidth, Integer maxHeight) {

        // 图片压缩：如果有限制大小
        if (hasValue(maxWidth, maxHeight)) {
            try {
                InputStream is = new FileInputStream(file);
                BufferedImage buff = ImageIO.read(is);

                if (buff.getHeight() > maxHeight || buff.getWidth() > maxWidth) {
                    Thumbnails.of(file).size(maxWidth, maxHeight).toFile(file);
                }

                logger.info("[zmn] 图片压缩，width: {}, height: {}，fileName: {} {}", maxWidth, maxHeight, directory, fileName);
            } catch (IOException e) {
                logger.error("[zmn]图片压缩异常：{}", e.getMessage());
            }
        }

        String path = getPath(directory, fileName);

        PutObjectRequest request = new PutObjectRequest(bucketName, path, file, null);

        return upload(request);
    }

    @Override
    public ResultDTO uploadPic(String directory, String fileName, InputStream inputStream) {
        return uploadPic(directory, fileName, inputStream, null, null);
    }

    @Override
    public ResultDTO uploadPicUnpressed(String directory, String fileName, InputStream inputStream) {
        return uploadPic(directory, fileName, inputStream, null, null);
    }

    @Override
    public ResultDTO uploadFile(String directory, String fileName, InputStream inputStream) {
        String path = getPath(directory, fileName);

        PutObjectRequest request = new PutObjectRequest(bucketName, path, inputStream, null);

        return uploadFile(request);
    }

    @Override
    public ResultDTO uploadPic(String directory, String fileName, InputStream inputStream, Integer maxWidth, Integer maxHeight) {

        // 图片压缩：如果有限制大小
        if (hasValue(maxWidth, maxHeight)) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len;
                while ((len = inputStream.read(buffer)) > -1) {
                    baos.write(buffer, 0, len);
                }
                baos.flush();

                inputStream = new ByteArrayInputStream(baos.toByteArray());
                BufferedImage buff = ImageIO.read(inputStream);
                if (buff.getHeight() > maxHeight || buff.getWidth() > maxWidth) {
                    inputStream = new ByteArrayInputStream(baos.toByteArray());
                    baos = new ByteArrayOutputStream();
                    Thumbnails.of(inputStream).size(maxWidth, maxHeight).toOutputStream(baos);
                    inputStream = new ByteArrayInputStream(baos.toByteArray());
                } else {
                    inputStream = new ByteArrayInputStream(baos.toByteArray());
                }

                logger.info("[zmn] 图片压缩，width: {}, height: {}，fileName: {} {}", maxWidth, maxHeight, directory, fileName);
            } catch (IOException e) {
                logger.error("[zmn]图片压缩异常：{}", e.getMessage());
            }
        }

        String path = getPath(directory, fileName);

        PutObjectRequest request = new PutObjectRequest(bucketName, path, inputStream, null);

        return upload(request);
    }

    private ResultDTO upload(PutObjectRequest request) {
        ResultDTO dto = new ResultDTO();
        dto.setStatusBool(false);

        String path = request.getKey();

        if (path.startsWith(GlobalDict.ALI_YUN_OSS_PIC_SPLIT)) {
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_BEGIN);
            return dto;
        }

        if (path.indexOf(URL_ERROR_SPLIT) != -1) {
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_SPLIT_FAILD);
            return dto;
        }

        if (!isPicture(path)) {
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_TYPE_FAILD);
            return dto;
        }

        CredentialsProvider provider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);

        OSSClient ossClient = new OSSClient(endpoint, provider, null);

        try {
            ossClient.putObject(request);
            dto.setStatusBool(true);
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_SUCCESS);
        } catch (OSSException e) {
            logger.error(e.getMessage());
            dto.setMessage(e.getMessage());
        } catch (ClientException e) {
            logger.error(e.getMessage());
            dto.setMessage(e.getMessage());
        } finally {
            ossClient.shutdown();
        }

        return dto;
    }

    private ResultDTO uploadFile(PutObjectRequest request) {
        ResultDTO dto = new ResultDTO();
        dto.setStatusBool(false);

        String path = request.getKey();

        if (path.startsWith(GlobalDict.ALI_YUN_OSS_PIC_SPLIT)) {
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_BEGIN);
            return dto;
        }

        if (path.indexOf(URL_ERROR_SPLIT) != -1) {
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_SPLIT_FAILD);
            return dto;
        }

        CredentialsProvider provider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);

        OSSClient ossClient = new OSSClient(endpoint, provider, null);

        try {
            ossClient.putObject(request);
            dto.setStatusBool(true);
            dto.setMessage(GlobalDict.ALI_YUN_OSS_PIC_SUCCESS);
        } catch (OSSException e) {
            logger.error(e.getMessage());
            dto.setMessage(e.getMessage());
        } catch (ClientException e) {
            logger.error(e.getMessage());
            dto.setMessage(e.getMessage());
        } finally {
            ossClient.shutdown();
        }

        return dto;
    }

    private boolean isPicture(String fileName) {
        if (fileName.toLowerCase().endsWith(GlobalDict.ALI_YUN_OSS_PIC_TYPE_GPG)) {
            return true;
        }
        if (fileName.toLowerCase().endsWith(GlobalDict.ALI_YUN_OSS_PIC_TYPE_GPEG)) {
            return true;
        }
        if (fileName.toLowerCase().endsWith(GlobalDict.ALI_YUN_OSS_PIC_TYPE_PNG)) {
            return true;
        }
        if (fileName.toLowerCase().endsWith(GIF_PICTURE)) {
            return true;
        }
        return false;
    }

    @Override
    public String getPicUrl(String directory, String fileName) {
        String uri = getPath(directory, fileName);

        return new StringBuilder(GlobalDict.ALI_YUN_OSS_PIC_BASE_URL).append(GlobalDict.ALI_YUN_OSS_PIC_SPLIT).append(uri).toString();
    }

    @Override
    public String getFileUrl(String directory, String fileName) {
        String uri = getPath(directory, fileName);

        return new StringBuilder(ALI_YUN_OSS_FILE_BASE_URL).append(GlobalDict.ALI_YUN_OSS_PIC_SPLIT).append(uri).toString();
    }

    @Override
    public AliyunOssStsDTO getStsAuther(String serviceId) {
        AliyunOssStsDTO checkDto = check(serviceId);
        if (checkDto != null) {
            return checkDto;
        }

        try {
            // 添加endpoint（直接使用STS endpoint，前两个参数留空，无需添加region ID）
            try {
                DefaultProfile.addEndpoint(GlobalConsts.SPLITER_EMPTY, GlobalConsts.SPLITER_EMPTY, product, stsEndpoint);
            } catch (com.aliyuncs.exceptions.ClientException e) {

            }

            // 构造default profile（参数留空，无需添加region ID）
            IClientProfile profile = DefaultProfile.getProfile(GlobalConsts.SPLITER_EMPTY, accessKeyId, accessKeySecret);

            // 用profile构造client
            DefaultAcsClient client = new DefaultAcsClient(profile);

            final AssumeRoleResponse response = post(serviceId, client);

            AliyunOssStsDTO dto = getSuccessDto(response);

            return dto;
        } catch (com.aliyuncs.exceptions.ClientException e) {
            AliyunOssStsDTO dto = new AliyunOssStsDTO();
            dto.setRequestId(e.getRequestId());
            dto.setErrCode(e.getErrCode());
            dto.setErrMsg(e.getErrMsg());

            logger.error("[zmn]APP上传时获取零时权限异常，msg: {}", dto);

            return dto;
        }
    }

    @NotNull
    private String getPath(String directory, String fileName) {
        StringBuilder sb = new StringBuilder(profile);

        if (!directory.startsWith(GlobalDict.ALI_YUN_OSS_PIC_SPLIT)) {
            sb.append(GlobalDict.ALI_YUN_OSS_PIC_SPLIT);
        }
        sb.append(directory);

        if (!directory.endsWith(GlobalDict.ALI_YUN_OSS_PIC_SPLIT)) {
            sb.append(GlobalDict.ALI_YUN_OSS_PIC_SPLIT);
        }
        sb.append(fileName);

        return sb.toString();
    }

    private AssumeRoleResponse post(String roleSessionName, DefaultAcsClient client) throws com.aliyuncs.exceptions.ClientException {
        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setMethod(MethodType.POST);
        request.setRoleArn(roleArn);
        request.setRoleSessionName(roleSessionName);
        request.setDurationSeconds((long)DateUtil.SECOND_OF_HOURS);

        return client.getAcsResponse(request);
    }

    @NotNull
    private AliyunOssStsDTO getSuccessDto(AssumeRoleResponse response) {
        AliyunOssStsDTO dto = new AliyunOssStsDTO();
        dto.setSuccess(Boolean.TRUE);
        dto.setExpiration(DateUtil.getTimeZone(response.getCredentials().getExpiration(), DateUtil.FORMAT_DEFAULT));
        dto.setAccessKeyId(response.getCredentials().getAccessKeyId());
        dto.setAccessKeySecret(response.getCredentials().getAccessKeySecret());
        dto.setSecurityToken(response.getCredentials().getSecurityToken());
        dto.setRequestId(response.getRequestId());
        return dto;
    }

    @Nullable
    private AliyunOssStsDTO check(String serviceId) {
        if (StringUtils.isEmpty(serviceId)) {
            AliyunOssStsDTO dto = new AliyunOssStsDTO();
            dto.setErrCode(String.valueOf(StatusConsts.STATUS_ERROR));
            dto.setErrMsg("参数：师傅ID不能为空");
            return dto;
        }
        return null;
    }

    @Nullable
    private ResultDTO validationPicMaxSize(Integer maxWidth, Integer maxHeight) {
        if (maxWidth == null || maxHeight < 100 || maxHeight == null || maxHeight < 100) {
            ResultDTO dto = new ResultDTO();
            dto.setStatusBool(false);
            dto.setMessage("上传图片大小限制不能小于100像素");
            return dto;
        }
        return null;
    }

    /**
     * 访问路经：http://img1.xiujiadian.com/dev/plat/test/test1.jpg
     *
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        // Endpoint以杭州为例，其它Region请按实际情况填写。
        String endpoint = "oss-cn-beijing.aliyuncs.com";
        // 云账号AccessKey有所有API访问权限，建议遵循阿里云安全最佳实践，创建并使用RAM子账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建。
        String accessKeyId = "LTAIO5vYQp99ShyP";
        String accessKeySecret = "gLNTcwIRgQiWN7RYmKNTprnJCLz3Kb";

        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

        // 上传文件流。
        InputStream inputStream = new FileInputStream("C:\\Users\\Lenovo\\Pictures\\test1.jpg");
        PutObjectResult rel = ossClient.putObject("zmnoss", "plat/test/test.jpg", inputStream);

        out.println(rel);

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    public static boolean hasValue(Integer maxWidth, Integer maxHeight) {
        return maxWidth != null && maxWidth > 0 && maxHeight != null && maxHeight > 0;
    }

}
